using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Collections.ObjectModel;
using System.Net.Mail;
using Decav.Gatsb.Mobile;
using Decav.Reality;

namespace Decav.Gatsb.Mobile
{
    /// <summary>
    /// The header of an SMTP header.
    /// </summary>
    [Serializable]
    public class SmtpMailHeader
    {
        private SmtpMailHeader()
        {
        }

        private Dictionary<string, List<string>> _values = new Dictionary<string, List<string>>(StringComparer.CurrentCultureIgnoreCase);
        private ContentType _type;

        /// <summary>
        /// Parses a mail header into an <see cref="SmtpMailHeader"/>.
        /// </summary>
        /// <param name="mailStream">The stream to parse the header from.</param>
        /// <returns>The header.</returns>
        public static SmtpMailHeader Parse(StreamReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException("reader");
        

            SmtpMailHeader header = new SmtpMailHeader();

            // The last key is the last place we put a value.  If we encounter a line
            // that starts with whitespace, this value is to be appended to the last value,
            // so we need the key that we will use to identify this.
            string lastKey = null;

            string line = null;
            while ((line = reader.ReadLine()) != null)
            {
                // Blan line is the end of the header.
                if (line.Trim().Length == 0)
                    break;

                // If this line starts with whitespace, then we need to
                // ad dit to the last line.
                List<string> valueList = null;
                if (line.TrimStart(' ', '\t').Length != line.Length)
                {
                    if (lastKey == null)
                        throw new InvalidOperationException("The first line of the SMTP header began with whitespace.  Header is invalid.");

                    valueList = header._values[lastKey];
                    valueList[valueList.Count - 1] = string.Format("{0}\r\n{1}", valueList[valueList.Count - 1], line.Trim());                    
                    continue;
                }

                // If there is no whitespace, then split on the first colon to get the name of the value, and then
                // set the value.
                string[] pair = line.Split(new char[] { ':' }, 2);
                string key = pair[0].Trim();

                if (!header._values.TryGetValue(key, out valueList))
                {
                    valueList = new List<string>();
                    header._values.Add(key, valueList);
                }

                valueList.Add(pair[1].Trim());
                lastKey = key;
            }

            // parse the content type.
            if (header._values.ContainsKey("content-type"))
            {
                header._type = ContentType.Parse(header._values["content-type"][0]);
            }
            else
            {
                header._type = ContentType.PlainText;
            }

            return header;
        }

        /// <summary>
        /// Gets a collection of the values that were parsed from the header for the key.
        /// </summary>
        /// <param name="key">The key to get the values for.</param>
        /// <returns>A read only collection of the value.</returns>
        public ReadOnlyCollection<string> this[string key]
        {
            get
            {
                if (key == null)
                    throw new ArgumentNullException("key");

                return new ReadOnlyCollection<string>(_values[key]);
            }
        }

        /// <summary>
        /// Checks if this header contains a value for the specified key.
        /// </summary>
        /// <param name="key">The key to lookup.</param>
        /// <returns>True when the header contains the key, else false.</returns>
        public bool Contains(string key)
        {
            if (key == null)
                throw new ArgumentNullException("key");

            return _values.ContainsKey(key);
        }

        /// <summary>
        /// Gets the sender of the message
        /// </summary>
        public EmailAddress Sender
        {
            get
            {
                if (!_values.ContainsKey("x-sender"))
                    throw new MessageParseException("The 'x-sender' header element was not parsed from the SmtpMailHeader.");

                return new EmailAddress(_values["x-sender"][0].Trim());
            }
        }

        /// <summary>
        /// Gets the message recipient.
        /// </summary>
        public EmailAddress Recipient
        {
            get
            {
                if (!_values.ContainsKey("x-receiver"))
                    throw new MessageParseException("The 'x-receiver' header element was not parsed from the SmtpMailHeader.");

                return new EmailAddress(_values["x-receiver"][0].Trim());
            }
        }

        /// <summary>
        /// Gets the type of the content that is in the message.
        /// </summary>
        public ContentType ContentType
        {
            get
            {
                return _type;
            }
        }

        /// <summary>
        /// Gets the date the message was received.
        /// </summary>
        public DateTime DateReceived
        {
            get
            {
                return DateTime.UtcNow;
            }
        }
    }
}
